#include "principalwindow.hpp"
#include <iostream>

#include <sys/types.h>
#include <unistd.h>


MainWindow::MainWindow(){
    Glib::RefPtr<Gnome::Glade::Xml> Widgetstree = Gnome::Glade::Xml::create("interface1.glade");

    Widgetstree->get_widget("principal", principalwindow);
    Widgetstree->get_widget("menuopen", menuopen);
    Widgetstree->get_widget("buttonmorphology", morphology);
    Widgetstree->get_widget("buttonconversion", conversion);
    Widgetstree->get_widget("buttonanalysis", analysis);
    Widgetstree->get_widget("result_window", resultwindow);
    Widgetstree->get_widget("treeview", imagetree);


    Widgetstree->get_widget("combo_structuretype", combo_structuretype);
    Widgetstree->get_widget("spinbutton_image", spinbutton_image);
    Widgetstree->get_widget("spinbutton_skel", spinbutton_skel);
    Widgetstree->get_widget("spinbutton_seeds", spinbutton_seeds);
    Widgetstree->get_widget("spinbutton_body", spinbutton_body);
    Widgetstree->get_widget("spinbutton_pointermap", spinbutton_pointermap);
    Widgetstree->get_widget("spinbutton_rcdata", spinbutton_rcdata);
    Widgetstree->get_widget("spinbutton_labelling", spinbutton_labelling);


    Widgetstree->get_widget("button_makepre", make_pre);
    Widgetstree->get_widget("button_makelabel", make_label);
    Widgetstree->get_widget("button_contour", make_contour);
    Widgetstree->get_widget("button_redo", make_redo);


    Widgetstree->get_widget("spinbuttonsamplex", sample_x);
    Widgetstree->get_widget("spinbuttonsampley", sample_y);
    Widgetstree->get_widget("spinbuttonradius1", radius1);
    Widgetstree->get_widget("spinbuttoncriticaldistance", criticaldist);
    Widgetstree->get_widget("spinbuttonsuper1", superposition1);
    Widgetstree->get_widget("spinbuttonsuper2", superposition2);
    Widgetstree->get_widget("spinbuttonacute", acute);
    Widgetstree->get_widget("spinbuttonopposite", opposite);
    Widgetstree->get_widget("spinbuttonerosion", erosion);
    Widgetstree->get_widget("spinbuttonareafilter", areafilter);
    Widgetstree->get_widget("spinbuttonstability", stability);
    Widgetstree->get_widget("spinbutton_prescale", prescale);



    sample_x->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    sample_y->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    radius1->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    criticaldist->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    superposition2->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    superposition1->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    acute->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    opposite->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    areafilter->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    stability->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));
    prescale->signal_value_changed().connect(sigc::mem_fun(*this, &MainWindow::changeparams));


    //Conecta Sinais aos Callbacks
    menuopen->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::openImage));
    morphology->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::openMorphPallet));
    conversion->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::openConvPallet));
    analysis->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::openAnalysisPallet));
    resultwindow->signal_toggled().connect(sigc::mem_fun(*this, &MainWindow::resultwindowchange));

    make_pre->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::makepreprocessing));
    make_label->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::makelabelling));
    make_contour->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::makecontour));
    make_redo->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::redo));
    //Define o valor padrao dentre os itens da lista combo box
    //Exibe toda interface

    new_windows=true;

    principalwindow->show_all();
    morphpallet=NULL;
    conversionpallet=NULL;
    analysispallet=NULL;
    emptyid=0;

    //Tree view setup
    treeModel = Gtk::ListStore::create(Columns);
    imagetree->set_model(treeModel);
    imagetree->append_column("ID", Columns.m_col_id);
    imagetree->append_column("Object", Columns.m_col_object);
    imagetree->append_column("Type", Columns.m_col_type);
    principalwindow->show_all_children();


    spinbutton_image->set_value(0);

    superposition1->set_range(0.0,1.0);
    superposition1->set_value(0.7);

    superposition2->set_range(-1.0,0);
    superposition2->set_value(-0.7);

    acute->set_range(-1.0,1.0);
    acute->set_value(0.0);

    opposite->set_range(-1.0,0.0);
    opposite->set_value(-0.7);

    Gtk::Main::run(*principalwindow);



}

void MainWindow::openMorphPallet(){
    if(morphpallet!=NULL){
        morphpallet->getWindow()->show_all();
    }
    else{
       morphpallet = new MorphPallet();
    }
}

void MainWindow::openImage(){
    Gtk::FileChooserDialog dialog("Please choose a file",
          Gtk::FILE_CHOOSER_ACTION_OPEN);
    dialog.set_transient_for(*(this->principalwindow));

    //Add response buttons the the dialog:
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
    dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

      //Show the dialog and wait for a user response:
    int result = dialog.run();

    //Handle the response:
    switch(result)
    {
        case(Gtk::RESPONSE_OK):
        {
            std::cout << "Open clicked." << std::endl;

            //Notice that this is a std::string, not a Glib::ustring.
            std::string filename = dialog.get_filename();
            objectsopen.push_back(new ImageWindow(filename,this));
            break;
        }
        case(Gtk::RESPONSE_CANCEL):
        {
            std::cout << "Cancel clicked." << std::endl;
            break;
        }
        default:
        {
            std::cout << "Unexpected button clicked." << std::endl;
            break;
        }
  }

  updateimagetree();


}

void MainWindow::openConvPallet(){
    if(conversionpallet!=NULL){
        conversionpallet->getWindow()->show_all();
    }
    else{
       conversionpallet = new ConversionPallet();
    }
}

void MainWindow::openAnalysisPallet(){
    if(analysispallet!=NULL){
        analysispallet->getWindow()->show_all();
    }
    else{
       analysispallet = new AnalysisPallet();
    }
}


void MainWindow::resultwindowchange(){
    new_windows=(!new_windows);
}

bool MainWindow::newwindow(){
    return new_windows;
}

void MainWindow::closethisObject(Objects* thisobject){
    objectsopen.remove(thisobject);
    delete thisobject;
    updateimagetree();
}

void MainWindow::updateimagetree(){
    treeModel->clear();
    list<Objects*>::iterator i;
    Gtk::TreeModel::Row row;
    for(i=objectsopen.begin();i!=objectsopen.end();i++){
        row = *(treeModel->append());
        row[Columns.m_col_id] = (*i)->getID();
        row[Columns.m_col_object] = (*i)->getType_string();
        if((*i)->getType()==Objects::IMAGE){
            ImageWindow* imagewindow = static_cast<ImageWindow*>(*i);
            row[Columns.m_col_type] = imagewindow->getImageType();
        }

    }
}

Objects* MainWindow::getThisfromID(unsigned int ID){
    list<Objects*>::iterator i;
    for(i=objectsopen.begin();i!=objectsopen.end();i++){
        if((*i)->getID()==ID)
                return (*i);
    }
    return NULL;
}

void MainWindow::makepreprocessing(){
    switch(combo_structuretype->get_active_row_number()){
        case 0: /*neurons*/
            preoptions.type=PreprocessingOption::NEURON;
            break;
        case 1: /*retinal*/
            preoptions.type=PreprocessingOption::RETINAL_VESSELS;
            break;
    }

     Objects* windowforimage = getThisfromID(spinbutton_image->get_value_as_int());

    if(windowforimage->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }


    Image* image = ((ImageWindow*)windowforimage)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    Binary_Image* body;
    Binary_Image* skel;
    Binary_Image* seed;

    preprocessing((Binary_Image*)image,&skel,&seed,&body,preoptions);


    spinbutton_body->set_value(emptyid);
    outImage(body);

    spinbutton_skel->set_value(emptyid);
    outImage(skel);

    spinbutton_seeds->set_value(emptyid);
    outImage(seed);



}

void MainWindow::makelabelling(){

    Binary_Image* skelbranch;
    Binary_Image* body;
    Binary_Image* seeds;

    Image* image;

    {
    Objects* windowforskel = getThisfromID(spinbutton_skel->get_value_as_int());

    if(windowforskel->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    image = ((ImageWindow*)windowforskel)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    skelbranch=(Binary_Image*)image->copy();

    }

    {
    Objects* windowforseed = getThisfromID(spinbutton_seeds->get_value_as_int());

    if(windowforseed->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    image = ((ImageWindow*)windowforseed)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    seeds=(Binary_Image*)image;
    }

    {
    Objects* windowforbody= getThisfromID(spinbutton_body->get_value_as_int());

    if(windowforbody->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    image = ((ImageWindow*)windowforbody)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    body=(Binary_Image*)image;
    }


    PositionMap* crossmap = new PositionMap(skelbranch->getHeight(),skelbranch->getWidth());
    Grey_Image* labelled = new Grey_Image(skelbranch->getHeight(),skelbranch->getWidth(),65500);
    RCData* rcdata = new RCData(skelbranch->getHeight(),skelbranch->getWidth());
    RCData* newrcdata = new RCData(skelbranch->getHeight(),skelbranch->getWidth());


    newlabelling(seeds,skelbranch,body,branchoptions,crossmap,rcdata,newrcdata,labelled);

    spinbutton_labelling->set_value(emptyid);
    outImage(labelled);

    Color_Image* colorlabelled=(Color_Image*)labelled->convert(Image::COLOR,1);
    outImage(colorlabelled);

    if(crossmap!=NULL){
        spinbutton_pointermap->set_value(emptyid);
        objectsopen.push_back(new PositionMapWindow(crossmap,this));
        updateimagetree();
    }

    if(rcdata!=NULL){
        spinbutton_rcdata->set_value(emptyid);
        objectsopen.push_back(new RCDataWindow(newrcdata,this));
        updateimagetree();
    }

    delete skelbranch;


}

void MainWindow::makecontour(){


    Objects* windowforlabelled= getThisfromID(spinbutton_labelling->get_value_as_int());

    if(windowforlabelled->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    Image* grey = ((ImageWindow*)windowforlabelled)->getImage();

    if(grey->getType()!=Image::GREY){
        invalidtypeerror();
        return;
    }

    Grey_Image* labelled=(Grey_Image*)grey;



    Objects* windowforcrossmap= getThisfromID(spinbutton_pointermap->get_value_as_int());

    if(windowforcrossmap->getType()!= Objects::POSITIONMAP){
        invalidobjecterror();
        return;
    }

    PositionMap* crossmap = ((PositionMapWindow*)windowforcrossmap)->getData();

    ContourExtractorBranch extractor;
    Contour* normal = extractor.getContour(labelled,crossmap);
    objectsopen.push_back(new ContourWindow(normal,this));
    updateimagetree();

    Objects* windowforimage = getThisfromID(spinbutton_image->get_value_as_int());

    if(windowforimage->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    Image* image = ((ImageWindow*)windowforimage)->getImage();

    Color_Image* img2xcolor;
    Binary_Image* img2x;

    Glib::RefPtr<Gdk::Pixbuf> *imgscale = imagetoPixbuf(image);
    Glib::RefPtr<Gdk::Pixbuf> *imgscale2x = new Glib::RefPtr<Gdk::Pixbuf>;
    *imgscale2x = (*imgscale)->scale_simple(preoptions.preprocessing_scale*image->getWidth(),preoptions.preprocessing_scale*image->getHeight(),Gdk::INTERP_NEAREST);
    img2xcolor=pixbuftoColor(imgscale2x);
    img2x=(Binary_Image*)img2xcolor->convert(Image::BINARY,0);

    delete imgscale;
    delete imgscale2x;
    delete img2xcolor;

    Contour* expanded = contourexpand(normal,img2x);
    objectsopen.push_back(new ContourWindow(expanded,this));
    updateimagetree();

}


void MainWindow::redo(){
    Binary_Image* skelbranch;
    Binary_Image* body;
    Binary_Image* seeds;

    Image* image;

    {
    Objects* windowforskel = getThisfromID(spinbutton_skel->get_value_as_int());

    if(windowforskel->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    image = ((ImageWindow*)windowforskel)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    skelbranch=(Binary_Image*)image;
    }

    {
    Objects* windowforseed = getThisfromID(spinbutton_seeds->get_value_as_int());

    if(windowforseed->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    image = ((ImageWindow*)windowforseed)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    seeds=(Binary_Image*)image;
    }

    {
    Objects* windowforbody= getThisfromID(spinbutton_body->get_value_as_int());

    if(windowforbody->getType()!= Objects::IMAGE){
        invalidobjecterror();
        return;
    }

    image = ((ImageWindow*)windowforbody)->getImage();

    if(image->getType()!=Image::BINARY){
        invalidtypeerror();
        return;
    }

    body=(Binary_Image*)image;
    }

    Objects* windowforrcdata= getThisfromID(spinbutton_rcdata->get_value_as_int());
    if(windowforrcdata->getType()!= Objects::RCDATA){
        invalidobjecterror();
        return;
    }
    RCData* rcdata = ((RCDataWindow*)windowforrcdata)->getData();

    PositionMap* crossmap = new PositionMap(skelbranch->getHeight(),skelbranch->getWidth());
    Grey_Image* labelled = new Grey_Image(skelbranch->getHeight(),skelbranch->getWidth(),65500);
    RCData* newrcdata = new RCData(skelbranch->getHeight(),skelbranch->getWidth());

    newlabelling(seeds,skelbranch,body,branchoptions,crossmap,rcdata,newrcdata,labelled);
    outImage(labelled);

    Color_Image* colorlabelled=(Color_Image*)labelled->convert(Image::COLOR,1);
    outImage(colorlabelled);


    if(crossmap!=NULL){
        spinbutton_pointermap->set_value(emptyid);
        objectsopen.push_back(new PositionMapWindow(crossmap,this));
        updateimagetree();
    }

    if(rcdata!=NULL){
        closethisObject(windowforrcdata);
        spinbutton_rcdata->set_value(emptyid);
        objectsopen.push_back(new RCDataWindow(newrcdata,this));
        updateimagetree();
    }

}


void MainWindow::invalidobjecterror(){

    Gtk::MessageDialog message("Invalid Object!",false,Gtk::MESSAGE_ERROR,Gtk::BUTTONS_CLOSE,true);
    message.run();
}

void MainWindow::invalidtypeerror(){

    Gtk::MessageDialog message("Invalid type! This operation don't act in current image type",false,Gtk::MESSAGE_ERROR,Gtk::BUTTONS_CLOSE,true);
    message.run();
}

void MainWindow::outImage(Image* img){
    if(img!=NULL){
        this->objectsopen.push_back(new ImageWindow(img,this));
        this->updateimagetree();
    }
    else{
        cout << "Error while perform image atualization" << std::endl;
    }
}

void MainWindow::forcercclassification(Position xy,CriticalRegion::RegionType type){
    Objects* windowforrcdata= getThisfromID(spinbutton_rcdata->get_value_as_int());
    if(windowforrcdata->getType()!= Objects::RCDATA){
        invalidobjecterror();
        return;
    }
    RCData* rcdata = ((RCDataWindow*)windowforrcdata)->getData();

    if(rcdata!=NULL){
        rcdata->forceclassification(xy,type);
    }

}

void MainWindow::changeparams(){

    preoptions.densitysamplex=sample_x->get_value_as_int();
    preoptions.densitysampley=sample_y->get_value_as_int();
    preoptions.circleradius=radius1->get_value_as_int();
    preoptions.erosionradius=erosion->get_value_as_int();
    preoptions.areafilter=areafilter->get_value_as_int();
    preoptions.preprocessing_scale=prescale->get_value();

    branchoptions.acute_threshold=(float)acute->get_value();
    branchoptions.critical_distance=criticaldist->get_value_as_int();
    branchoptions.opposite_threshold=(float)opposite->get_value();
    branchoptions.superposition_threshold_direct=(float)superposition1->get_value();
    branchoptions.superposition_threshold_opposite=(float)superposition2->get_value();
    branchoptions.stabilitycondition=stability->get_value_as_int();
}
